﻿using AnimatedGif;
using MatrixControls;
using MatrixEditor.Properties;
using System;
using System.ComponentModel;
using System.Drawing;
using System.Threading;
using System.Windows.Forms;
using static MatrixEditor.Utilities;
using static MatrixEditor.Global;

namespace MatrixEditor
{
	public partial class FrmPlayer : Form
	{
		private readonly BackgroundWorker worker = new BackgroundWorker();

		private int current_frame      = 0;
		private string output_filename = null;

		private bool export_preview     = true;
		private bool export_loop_period = true;


		public FrmPlayer()
		{
			InitializeComponent();

			worker.WorkerSupportsCancellation = true;

			worker.DoWork             += new DoWorkEventHandler(PlayFramesEvent);
			worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(PlayFramesStoppedEvent);
		}


		#region 自定义函数
		/// <summary>
		/// 刷新显示当前缩略图
		/// </summary>
		public void RefreshThumbnail()
		{
			if (Frames.Count > 0 && Visible)
			{
				if (current_frame >= Frames.Count) { current_frame = 0; }

				ThumbnailPreview.DrawPixels(Frames[current_frame], Colors, SingleColor);

				BeginInvoke(new Action(() =>
				{
					LabelFrameCount.Text = $"{current_frame + 1}/{Frames.Count}";
				}));
			}
		}
		
		/// <summary>
		/// 播放/暂停所有帧缩略图
		/// </summary>
		public void PlayFrames()
		{
			(worker.IsBusy ? new Action(() => worker.CancelAsync()) : () => worker.RunWorkerAsync())();
			UpdateControlsStatus();
		}

		/// <summary>
		/// 显示下一帧或上一帧缩略图
		/// </summary>
		/// <param name="next">为 true 时显示下一帧，为 false 时显示上一帧</param>
		public void ShowFrame(bool next=true)
		{
			if (next) {
				current_frame = ++current_frame % Frames.Count;
			} else {
				current_frame = --current_frame % Frames.Count;

				if (current_frame < 0) { current_frame += Frames.Count; }
			}

			RefreshThumbnail();
		}

		public void UpdateControlsStatus()
		{
			LabelPlay.Image = worker.IsBusy ? Resources.Pause : Resources.Play;

			LabelPrev.Enabled        = !worker.IsBusy;
			LabelNext.Enabled        = !worker.IsBusy;
			TextBoxPeriod.Enabled    = !worker.IsBusy;
			ButtonExport.Enabled     = !worker.IsBusy;
			ButtonExportMenu.Enabled = !worker.IsBusy;

			TextBoxPeriod.Text = Period.ToString();
		}
		#endregion


		#region 动态添加组件的响应事件
		private void PlayFramesEvent(object sender, DoWorkEventArgs e)
		{
			while (true)
			{
				if (worker.CancellationPending == true)
				{
					e.Cancel = true;
					break;
				}
				else
				{
					ShowFrame();
					Thread.Sleep(Period);
				}
			}
		}

		private void PlayFramesStoppedEvent(object sender, RunWorkerCompletedEventArgs e)
		{
			UpdateControlsStatus();
		}

		private void ValidatedPeriodTextbox()
		{
			if (string.IsNullOrEmpty(TextBoxPeriod.Text))
			{
				Period = DefaultPeriod;
				TextBoxPeriod.Text = DefaultPeriod.ToString();
			}
		}

		public void ChagnePeroidValue(bool increase=true)
		{
			ValidatedPeriodTextbox();

			int offset = increase ? 20 : -20;
			int value  = Period + offset;

			if (value <= 0) { value = 20; }

			Period = value;
			TextBoxPeriod.Text = value.ToString();
		}
		#endregion


		#region 窗体控件事件
		private void LabelPlay_Click(object sender, EventArgs e) => PlayFrames();

		private void LabelNext_Click(object sender, EventArgs e) => ShowFrame();

		private void LabelPrev_Click(object sender, EventArgs e) => ShowFrame(false);

		private void LabelNext_DoubleClick(object sender, EventArgs e) => ShowFrame();

		private void LabelPrev_DoubleClick(object sender, EventArgs e) => ShowFrame(false);

		private void LabelPrev_MouseEnter(object sender, EventArgs e) => LabelPrev.BackColor = Color.Yellow;

		private void LabelPrev_MouseLeave(object sender, EventArgs e) => LabelPrev.BackColor = SystemColors.Control;

		private void LabelPlay_MouseEnter(object sender, EventArgs e) => LabelPlay.BackColor = Color.Lime;

		private void LabelPlay_MouseLeave(object sender, EventArgs e) => LabelPlay.BackColor = SystemColors.Control;

		private void LabelNext_MouseEnter(object sender, EventArgs e) => LabelNext.BackColor = Color.Yellow;

		private void LabelNext_MouseLeave(object sender, EventArgs e) => LabelNext.BackColor = SystemColors.Control;

		private void TextBoxPeriod_Click(object sender, EventArgs e) { TextBoxPeriod.SelectAll(); }

		private void TextBoxPeriod_KeyPress(object sender, KeyPressEventArgs e)
		{
			if (!char.IsNumber(e.KeyChar) && !char.IsControl(e.KeyChar))
			{
				e.Handled = true;
			}
		}

		private void TextBoxPeriod_Validated(object sender, EventArgs e) => ValidatedPeriodTextbox();

		private void TextBoxPeriod_TextChanged(object sender, EventArgs e)
		{
			FileChanged = true;
			(Owner as FrmMain).UpdateFormTitle();
		}

		private void ButtonExport_Click(object sender, EventArgs e)
		{
			bool result   = false;
			var thumbnail = new Thumbnail
			{
				ShowBorder = false,
				BoxWidth = ThumbnailPreview.BoxWidth,
			};

			ButtonExport.Enabled = !ButtonExport.Enabled;

			using (var dialog = new SaveFileDialog())
			{
				dialog.DefaultExt = "gif";
				dialog.CheckFileExists = false;
				dialog.CheckPathExists = true;
				dialog.OverwritePrompt = true;
				dialog.RestoreDirectory = true;
				dialog.Filter = "动画文件 (*.gif)|*.gif|所有文件 (*.*)|*.*";
				dialog.FileName = output_filename;

				var save_result = dialog.ShowDialog();

				if (save_result == DialogResult.OK)
				{
					output_filename = dialog.FileName;
					result = true;
				}
			};

			if (result)
			{
				using (var gif = AnimatedGif.AnimatedGif.Create(output_filename, Period))
				{
					int delay = -1;

					for (int index = 0; index < Frames.Count; index++)
					{
						var bitmap = new Bitmap(thumbnail.ClientSize.Width, thumbnail.ClientSize.Height);

						if (index == Frames.Count - 1 && export_loop_period) { delay = 1000; }

						thumbnail.DrawPixels(Frames[index], Colors);
						thumbnail.DrawToBitmap(bitmap, thumbnail.ClientRectangle);

						gif.AddFrame(bitmap, delay, GifQuality.Bit8);
					}
				}

				if (export_preview) { OpenUrlOrFile(output_filename); }
			}

			ButtonExport.Enabled = !ButtonExport.Enabled;
		}

		private void ButtonExportMenu_Click(object sender, EventArgs e)
		{
			MenuStripExport.Show((Button)sender, 0, ((Button)sender).Height);
		}

		private void FrmPlayer_Load(object sender, EventArgs e)
		{
			export_preview     = Settings.Default.ExportPreview;
			export_loop_period = Settings.Default.ExportLoopPeriod;
		}

		private void FrmPlayer_KeyUp(object sender, KeyEventArgs e)
		{
			switch (e.KeyCode)
			{
				case Keys.W:
				case Keys.Up:
					ChagnePeroidValue(true);
					break;
				case Keys.S:
				case Keys.Down:
					ChagnePeroidValue(false);
					break;
				case Keys.A:
				case Keys.Left:
					if (!TextBoxPeriod.Focused) { ShowFrame(false); }
					break;
				case Keys.D:
				case Keys.Right:
					if (!TextBoxPeriod.Focused) { ShowFrame(); }
					break;
				case Keys.Enter:
				case Keys.Space:
					PlayFrames();
					break;
			}
		}

		private void FrmPlayer_VisibleChanged(object sender, EventArgs e)
		{
			RefreshThumbnail();

			if (worker.IsBusy) { PlayFrames(); }

			(Owner as FrmMain).CheckMainFormStatus();

			Settings.Default.PlayerVisible = Visible;
			Settings.Default.Save();
		}

		private void FrmPlayer_FormClosing(object sender, FormClosingEventArgs e)
		{
			if (worker.IsBusy) { PlayFrames(); }

			if (e.CloseReason == CloseReason.UserClosing)
			{
				Visible = false;
				e.Cancel = true;

				(Owner as FrmMain).CheckMainFormStatus();
			}
		}
		#endregion


		#region 菜单控件事件
		private void MenuItemHide_Click(object sender, EventArgs e)
		{
			(Owner as FrmMain).MenuItemViewPlayer_Click(sender, e);
		}

		private void MenuItemData_Click(object sender, EventArgs e)
		{
			var frmMain = Owner as FrmMain;

			(frmMain.DataForm.Visible ?
				new Action(
					() => frmMain.DataForm.BringToFront()
				) :
				() => frmMain.MenuItemViewData_Click(sender, e)
			)();
		}

		private void MenuStripExport_Opened(object sender, EventArgs e)
		{
			MenuItemExportPreview.Checked    = export_preview;
			MenuItemExportLoopPeriod.Checked = export_loop_period;
		}

		private void MenuStripExport_Closed(object sender, ToolStripDropDownClosedEventArgs e)
		{
			export_preview     = MenuItemExportPreview.Checked;
			export_loop_period = MenuItemExportLoopPeriod.Checked;
		}

		private void MenuItemExportPreview_MouseDown(object sender, MouseEventArgs e)
		{
			export_preview = MenuItemExportPreview.Checked = !MenuItemExportPreview.Checked;

			Settings.Default.ExportPreview = export_preview;
			Settings.Default.Save();
		}

		private void MenuItemExportLoopPeriod_MouseDown(object sender, MouseEventArgs e)
		{
			export_loop_period = MenuItemExportLoopPeriod.Checked = !MenuItemExportLoopPeriod.Checked;

			Settings.Default.ExportLoopPeriod = export_loop_period;
			Settings.Default.Save();
		}
		#endregion
	}
}
